package com.ken.common.web.log;

import org.slf4j.MDC;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * SpringBoot事件的监听器
 *
 * ApplicationEnvironmentPreparedEvent - 应用环境准备事件
 */
public class LogMDCListener implements GenericApplicationListener {

    private static final String APPLICATION_CONFIG_NAME = "configurationProperties";

    private static final String LOG_NAME = "logging.file.name";

    private static final String LOG_PATH = "spring.application.name";

    private static final Map<String, String> cache = new HashMap<>();

    /**
     * 设置当前要监听什么类型的事件
     * @param eventType
     * @return
     */
    @Override
    public boolean supportsEventType(ResolvableType eventType) {
        //A.class.isAssignableFrom(B.class)
        //如果A类型是B类型的父类(接口、同一个类型),则返回true
        return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType.getRawClass());
    }

    /**
     * 监听到具体事件的处理方法
     * @param event
     */
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        String logName = cache.get("logName");
        String logPath = cache.get("logPath");
        //第一次会去读取环境变量中的属性
        if (StringUtils.isEmpty(logName) || StringUtils.isEmpty(logPath)) {
            //获得当前微服务的名字
            ApplicationEnvironmentPreparedEvent environmentPreparedEvent = (ApplicationEnvironmentPreparedEvent) event;
            //获得配置环境对象
            ConfigurableEnvironment environment = environmentPreparedEvent.getEnvironment();
            //获取属性源集合 - SpringBoot的各种属性来源（application.yml...）
            MutablePropertySources propertySources = environment.getPropertySources();

            PropertySource<?> propertySource = propertySources.get(APPLICATION_CONFIG_NAME);
            logName = (String) propertySource.getProperty(LOG_NAME);
            logPath = (String) propertySource.getProperty(LOG_PATH);
            //如果没有设置日志文件名称，则默认采用微服务的名称作为日志文件名
            if (StringUtils.isEmpty(logName)) {
                logName = logPath;
            }

            //有值之后进行缓存
            cache.put("logName", logName);
            cache.put("logPath", logPath);
        }

        //设置MDC
        MDC.put("logPath", logPath);
        MDC.put("logName", logName);
    }

    /**
     * 控制监听器的顺序 值越小，优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 19;
    }
}
